# coding:utf-8
'''
Created on 2010-1-9

@author: Jason Green
@author-email: guileen@gmail.com
'''
from django.utils.translation import ugettext, ungettext
from django.utils.tzinfo import LocalTimezone
from django.template import defaultfilters
from django.utils.encoding import smart_unicode
import datetime

def get_deep_attr(top, field):
    '''
    usage: get_deep_attr(top,'user.groups')
    @param top:
    @param field:
    '''
    v = top
    for fld in field.split('__'):
        top = v
        if top:
            if(hasattr(top, fld)):
                v = getattr(top, fld, None)
            else:
                v = hasattr(top, 'get') and top.get(fld, None)
        else:
            v = None
    from django.db.models.manager import Manager
    if(isinstance(v, Manager)):
        v = v.all()
    return (top, v)


def get_default_fields(model, show_type='list'):
    '''
    usage: 
    @param model:
    @param show_type:
    '''
    attr = 'generic_%s_fields' % show_type
    if hasattr(model._meta, attr):
        return getattr(model._meta, attr)
    opts = model._meta
    fields = [f.name for f in opts.fields]
    if show_type == 'detail':
        fields += [f.name for f in opts.many_to_many]
    return fields

def get_fields_dict(model):
    opts = model._meta
    if(not hasattr(opts, '_fields_dict')):
        opts._fields_dict = dict([(f.name, f) for f in opts.fields])
        for f in opts.many_to_many :
            opts._fields_dict[f.name] = f
    return opts._fields_dict

def get_field(model, field_name):
    return get_fields_dict(model).get(field_name)

def get_deep_field(model, field_name):
    res = ()
    field = None
    for fld in field_name.split('__'):
        field = get_field(model, fld)
        assert field, 'could not find field of %s on model of %s' % (fld, model) 
        res += (field,)
        if field.rel:
            model = field.rel.to
    return res

#VERBOSE_NAME_LIST=(_('%{1}s'),_('%{2}s of %{1}s'),_('%{2}s\'s %{3}s of %{1}s'),)
def get_verbose_name(model, field_name=None):
    if field_name == None:
        return model._meta.verbose_name
    res = ()
    for f in get_deep_field(model, field_name):
        res += (unicode(f.verbose_name),)
    return '-'.join(res)

def is_bool(obj):
    return isinstance(obj, bool)

def is_date(obj):
    return isinstance(obj, datetime.date)

def is_datetime(obj):
    return isinstance(obj, datetime.datetime)

def is_time(obj):
    return isinstance(obj, datetime.time)

def timesince(d, now=None):
    """
    Takes two datetime objects and returns the time between d and now
    as a nicely formatted string, e.g. "10 minutes".  If d occurs after now,
    then "0 minutes" is returned.

    Units used are years, months, weeks, days, hours, and minutes.
    Seconds and microseconds are ignored.  Up to two adjacent units will be
    displayed.  For example, "2 weeks, 3 days" and "1 year, 3 months" are
    possible outputs, but "2 weeks, 3 hours" and "1 year, 5 days" are not.

    Adapted from http://blog.natbat.co.uk/archive/2003/Jun/14/time_since
    """
    chunks = (
      (60 * 60 * 24 * 365, lambda n: ungettext('year', 'years', n)),
      (60 * 60 * 24 * 30, lambda n: ungettext('month', 'months', n)),
      (60 * 60 * 24 * 7, lambda n : ungettext('week', 'weeks', n)),
      (60 * 60 * 24, lambda n : ungettext('day', 'days', n)),
      (60 * 60, lambda n: ungettext('hour', 'hours', n)),
      (60, lambda n: ungettext('minute', 'minutes', n))
    )
    # Convert datetime.date to datetime.datetime for comparison.
    if not isinstance(d, datetime.datetime):
        d = datetime.datetime(d.year, d.month, d.day)
    if now and not isinstance(now, datetime.datetime):
        now = datetime.datetime(now.year, now.month, now.day)

    if not now:
        if d.tzinfo:
            now = datetime.datetime.now(LocalTimezone(d))
        else:
            now = datetime.datetime.now()

    # ignore microsecond part of 'd' since we removed it from 'now'
    delta = now - (d - datetime.timedelta(0, 0, d.microsecond))
    since = delta.days * 24 * 60 * 60 + delta.seconds
    if since <= 0:
        # d is in the future compared to now, stop processing.
        return u'0 ' + ugettext('minutes')
    for (seconds, name) in chunks: #IGNORE:W0621
        count = since // seconds
        if count != 0:
            return ugettext('%(number)d %(type)s') % {'number': count, 'type': name(count)}

def since_until(value, now=None): #IGNORE:W0621
    if not value:
        return u''
    if not now:
        if value.tzinfo:
            now = datetime.datetime.now(LocalTimezone(value))
        else:
            now = datetime.datetime.now()
            
    delta = value > now and (value - now) or (now - value)
    if(delta < datetime.timedelta(0, 120, 0)): 
        return value > now and ugettext('Coming up') or ugettext('Just now')
    if(delta > datetime.timedelta(7, 0, 0)):
        return defaultfilters.date(value, ugettext("M.d,Y")) + " " + defaultfilters.date(value, ugettext("H:i"))
    return value > now and ugettext('%s after') % timesince(now, value) or ugettext('%s ago') % timesince(value, now)


def text(obj,field=None):
    if(field!=None):
        return text(get_deep_attr(obj, field)[1])
    
    if is_bool(obj) and obj:
        return obj and '<span class="v-true">Yes</span>' or '<span class="v-false">No</span>'
    elif obj == None:
        return '<span class="v-none">N/A</span>'
    elif is_datetime(obj):
        return since_until(obj)
    elif is_date(obj):
        return defaultfilters.date(obj, ugettext("M.d,Y"))
    elif is_time(obj):
        return defaultfilters.time(obj, ugettext("H:i"))
    else:
        return smart_unicode(obj)

def topyobj(code):
    try:
        obj = eval(code)
    except SyntaxError:
        print code
        return None
    return obj

fields_of = get_default_fields
field_turple = get_deep_field
name = get_verbose_name
value = get_deep_attr
